/*
 * Copyright © 2022-now 尛飛俠（Denvie） All rights reserved.
 */

package cn.denvie.springcloud.seata.gateway.filter;

import cn.denvie.springcloud.seata.gateway.utils.JwtUtils;
import cn.denvie.springcloud.seata.gateway.properties.NotAuthUrlProperties;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.security.PublicKey;
import java.util.Map;

/**
 * 自定义权限验证全局过滤器。
 *
 * @author Denvie
 * @date 2022/4/9
 * @since 1.0.0
 */
@Slf4j
@Order(Integer.MIN_VALUE)
@Component
public class AuthenticationGlobalFilter implements GlobalFilter, InitializingBean {
    /**
     * 请求各个微服务不需要用户认证的URL
     */
    @Autowired
    private NotAuthUrlProperties notAuthUrlProperties;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * jwt的公钥，需要网关启动时远程调用认证中心去获取公钥
     */
    private PublicKey publicKey;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 过滤不需要认证的URL，比如：/oauth/**
        String currentUrl = exchange.getRequest().getURI().getPath();
        boolean skip = shouldSkip(currentUrl);
        log.debug("请求的URL：{}，需要认证：{}", currentUrl, skip);
        if (skip) {
            return chain.filter(exchange);
        }

        // 获取token
        // 从请求头中解析 Authorization，value为：bearer XXXXXXXXXXXXXX
        // 或者从请求参数中解析 access_token
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");

        if (StringUtils.isEmpty(authHeader)) {
            throw new RuntimeException("需要认证的URL，请求头Authorization为空");
        }

        // 拿到token后，通过公钥（需要从授权服务器获取公钥）校验
        // 校验失败或超时会抛出异常
        Claims claims = JwtUtils.validateJwtToken(authHeader, publicKey);

        // 校验通过后，从token中获取的用户登录信息存储到请求头中
        ServerWebExchange newExchange = wrapHeader(exchange, claims);

        return chain.filter(newExchange);
    }

    private ServerWebExchange wrapHeader(ServerWebExchange serverWebExchange, Claims claims) {
        String userId = claims.get("additionalInfo", Map.class).get("userId").toString();
        String nickname = claims.get("additionalInfo", Map.class).get("nickname").toString();
        ServerHttpRequest request = serverWebExchange.getRequest().mutate()
                .header("username", claims.get("user_name", String.class))
                .header("userId", userId)
                .header("nickname", nickname)
                .build();
        // 将现在的 request 变成 exchange 对象
        return serverWebExchange.mutate().request(request).build();
    }

    /**
     * 不需要授权的路径
     */
    private boolean shouldSkip(String currentUrl) {
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
            if (pathMatcher.match(skipPath, currentUrl)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 获取公钥
        this.publicKey = JwtUtils.genPublicKey(restTemplate);
    }
}
